# -*- coding:utf-8 -*-
"""
Created on 31/lug/2011

@author: Luigi Poderico
"""

import random

import Entities
import Constants
import Parameters

def randomSize(aMaxSize):
    myMaxSize = round(aMaxSize*float(Parameters.ProblemSize)/10)
    myMinSize = round(myMaxSize * Parameters.Whide)
    
    return random.randint(myMinSize, myMaxSize)
    pass

#------------------------------------------------------------------------------ 

def randomFlag(aFlagRatio):
    if random.random()<aFlagRatio:
        return 0
    return 1

#------------------------------------------------------------------------------ 

def randomWeight(aTriangularParams):
    myWeight = random.triangular(aTriangularParams[0], aTriangularParams[2],
                             aTriangularParams[1])
    myWeight = int (myWeight) 
    return myWeight
                             
#===============================================================================
# 
#===============================================================================

class ResourceGenerator(object):
    
    theResourceNumber = 0
    
    def generate(self):
        ResourceGenerator.theResourceNumber = randomSize(Constants.MaxResources)
        myResources = []
        
        for _ in range(ResourceGenerator.theResourceNumber):
            myResource = self.__randomResource()
            myResources.append(myResource)
            pass
        return myResources
        pass
    
#------------------------------------------------------------------------------ 
    def __randomResource(self):
        myResource = Entities.Resource()
        myResource.theTransientFlag = randomFlag(Parameters.TransientRatio)
        myResource.theWeightLoadCost = int(randomWeight(Parameters.WeightLoadCost))
        
        return myResource
        pass
    
#------------------------------------------------------------------------------ 

    @staticmethod
    def getResQuantity(aResourceId):
        assert 0 <= aResourceId < ResourceGenerator.theResourceNumber
        myFactor = 1 + (4*aResourceId/ResourceGenerator.theResourceNumber)
        myMinRes = int(2 * myFactor)
        myModRes = int(4 * myFactor)
        myMaxRes = int(6 * myFactor)
        
        myResQuantity = int(random.triangular(myMinRes, myMaxRes, myModRes))
        
        return myResQuantity
    
    pass #class


#===============================================================================
# 
#===============================================================================

class MachineGenerator:
    theMachineNumber = 0
    
    def generate(self, aNumLocations, aNumNeighborhood):
        MachineGenerator.theMachineNumber = randomSize(Constants.MaxMachines)
        myMachines = []
        
        for myMachineId in range(MachineGenerator.theMachineNumber):
            myMachine = self.__randomMachine(myMachineId,
                                             aNumLocations,
                                             aNumNeighborhood)
            myMachines.append(myMachine)
            pass
        return myMachines
    
#------------------------------------------------------------------------------ 

    def __randomMachine(self, aMachineId, aNumLocations, aNumNeighborhood):
        myMachine = Entities.Machine()

        myMachine.theNeighborhoodId = random.randint(1, aNumNeighborhood)
        myMachine.theLocationId = random.randint(1, aNumLocations)
        
        self.__setCapacities(myMachine)
        self.__setSafetyCapacities(myMachine)
        self.__setMachineMoveCost(myMachine, aMachineId)
        
        return myMachine
    
#------------------------------------------------------------------------------ 
    
    def __setCapacities(self, aMachine):
        for myResId in range(ResourceGenerator.theResourceNumber):
            myResQuantity = ResourceGenerator.getResQuantity(myResId)
            aMachine.theCapacities.append(myResQuantity)
            pass
        pass
    
#------------------------------------------------------------------------------ 
    
    def __setSafetyCapacities(self, aMachine):
        assert ResourceGenerator.theResourceNumber == len(aMachine.theCapacities)
        for myResId in range(ResourceGenerator.theResourceNumber):
            mySafetyCapacity = int(aMachine.theCapacities[myResId] * \
                randomWeight(Parameters.SafetyResourceRatio))
            aMachine.theSafetyCapacties.append(mySafetyCapacity)
            pass
        pass
    
#------------------------------------------------------------------------------ 
    
    def __setMachineMoveCost(self, aMachine, aMachineId):
        for _ in range(MachineGenerator.theMachineNumber):
            myMMC = int(randomWeight(Parameters.MachineMoveCost))
            aMachine.theMachineMoveCost.append(myMMC)
            pass
        aMachine.theMachineMoveCost[aMachineId] = 0
        pass
    
    pass #class

#===============================================================================
# 
#===============================================================================

class ServiceGenerator:
    
    theServiceNo = 0
    
    def generate(self):
        ServiceGenerator.theServiceNo = randomSize(Constants.MaxServices)
        myServices = []
        
        for myServiceNo in range(ServiceGenerator.theServiceNo):
            myService = self.__randomService(myServiceNo)
            myServices.append(myService)
            pass
        return myServices
        pass
#------------------------------------------------------------------------------ 

    def __randomService(self, aServiceNumber):
        myService = Entities.Service()
        myService.theSpreadMin = randomWeight(Parameters.SpreadService)
        myDependingServicesNo = int(aServiceNumber*Parameters.MaxServicesDependRatio)
        myDependingServicesNo = random.randint(0, myDependingServicesNo)
        myDependingServices = random.sample(range(aServiceNumber),
                                            myDependingServicesNo)
        myService.theDependingServices = myDependingServices
        return myService
        pass
    
    pass #class

#===============================================================================
# 
#===============================================================================

class ProcessGenerator(object):
    def generate(self):
        myProcessesNumber = randomSize(Constants.MaxProcesses)
        myProcesses = []
        
        for myProcessNo in range(myProcessesNumber):
            myProcess = self.__randomProces(myProcessNo)
            myProcesses.append(myProcess)
            pass
        return myProcesses
    
#------------------------------------------------------------------------------ 

    def __randomProces(self, aProcessNo):
        myProcess = Entities.Process()
        
        myProcess.theServiceId = random.randint(1,
                                                ServiceGenerator.theServiceNo)
        self.__setRequirements(myProcess)
        myProcess.theProcessMovementCost = randomWeight(Parameters.ProcessMoveCost)
        return myProcess
        pass
    
#------------------------------------------------------------------------------ 
    
    def __setRequirements(self, aProcess):
        aProcess.theRequirements = []
        for myResId in range(ResourceGenerator.theResourceNumber):
            myResQuantity = ResourceGenerator.getResQuantity(myResId)/2
            aProcess.theRequirements.append(myResQuantity)
            pass
        pass
    
    pass

#===============================================================================
# 
#===============================================================================

class BalanceObjectiveGenerator(object):
    
    def __init__(self):
        self.theResourceCouple = {}
        pass
    
#------------------------------------------------------------------------------ 
    
    def generate(self):
        myBalObjsNo = randomSize(Constants.MaxBalanceCosts)
        myBalanceObjs = []
        
        for myBalObjNo in range(myBalObjsNo):
            myBalanceObj = self.__randomBalanceObj(myBalObjNo)
            myBalanceObjs.append(myBalanceObj)
            pass
        
        return myBalanceObjs
        pass
    
#------------------------------------------------------------------------------ 

    def __randomBalanceObj(self, aBalObjNo):
        myRandomBO = Entities.BalanceObjective()
        
        myResourceJ = None
        myResourceK = None
        while True:
            myResourceJ = random.randint(1, ResourceGenerator.theResourceNumber)
            myResourceK = random.randint(1, ResourceGenerator.theResourceNumber)
            if myResourceJ in self.theResourceCouple and \
                self.theResourceCouple[myResourceJ] == myResourceK:
                continue
            break
            pass
        myRandomBO.theResourceJ = myResourceJ
        myRandomBO.theResourceK = myResourceK
        myRandomBO.theTarget = randomWeight(Parameters.TargetBalanceCost)
        myRandomBO.theWeightBalanceCost = randomWeight(Parameters.WeightBalanceCost)
        
        return myRandomBO
        pass
    
    pass #class

#===============================================================================
# 
#===============================================================================

class Generator(object):
    
    theResourceGenerator = ResourceGenerator()
    theMachineGenerator = MachineGenerator()
    theServiceGenerator = ServiceGenerator()
    theProcesGenerator = ProcessGenerator()
    theBalanceObjectiveGenerator = BalanceObjectiveGenerator()
    
#------------------------------------------------------------------------------ 

    def __init__(self):
        pass
#------------------------------------------------------------------------------ 
    
    def generate(self):
        myProblemInstance = Entities.ProblemInstance()
        self.__addNumbers(myProblemInstance)
        self.__addResources(myProblemInstance)
        self.__addMachines(myProblemInstance)
        self.__addServices(myProblemInstance)
        self.__addProcesses(myProblemInstance)
        self.__addBalanceObjectives(myProblemInstance)
        self.__addWeights(myProblemInstance)
        
        return myProblemInstance
        pass

#------------------------------------------------------------------------------ 

    def __addNumbers(self, aProblemInstance):
        myNumLocations = randomSize(Constants.MaxLocations)
        myNumNeighborhood = randomSize(Constants.MaxNeighborhoods)
        aProblemInstance.addNumbers(myNumLocations, myNumNeighborhood)
        pass
    
#------------------------------------------------------------------------------ 

    def __addResources(self, aProblemInstance):
        myResources = self.theResourceGenerator.generate()
        aProblemInstance.addResources(myResources)
        pass
    
#------------------------------------------------------------------------------ 

    def __addMachines(self, aProblemInstance):
        myNumLocations = aProblemInstance.getNumLocations()
        myNumNeighborhood = aProblemInstance.getNumNeighborhood()
        
        myMachines = self.theMachineGenerator.generate(myNumLocations,
                                                       myNumNeighborhood)
        aProblemInstance.addMachines(myMachines)
        pass
    
#------------------------------------------------------------------------------ 

    def __addServices(self, aProblemInstance):
        myServices = self.theServiceGenerator.generate()
        aProblemInstance.addServices(myServices)
        pass
        
#------------------------------------------------------------------------------ 

    def __addProcesses(self, aProblemInstance):
        myProcesses = self.theProcesGenerator.generate()
        aProblemInstance.addProcess(myProcesses)
        pass
    
#------------------------------------------------------------------------------ 

    def __addBalanceObjectives(self, aProblemInstance):
        myBalanceObjectives = self.theBalanceObjectiveGenerator.generate()
        aProblemInstance.addBalanceObjectives(myBalanceObjectives)
        pass
    
#------------------------------------------------------------------------------ 

    def __addWeights(self, aProblemInstance):
        myWProcessMoveCost = randomWeight(Parameters.WeightProcessMoveCost)
        myWServiceMoveCost = randomWeight(Parameters.WeightServiceMoveCost)
        myWMachineMoveCost = randomWeight(Parameters.WeightMachineMoveCost)
        
        aProblemInstance.addWeights(myWProcessMoveCost,
                                    myWServiceMoveCost,
                                    myWMachineMoveCost)
        pass
    
    pass #class
